﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;

using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Outcoder;
using Outcoder.ApplicationModel;
using Outcoder.Services;

namespace CalciumTemplateApp
{
	public partial class App : Application
	{
		/// <summary>
		///     Constructor for the Application object.
		/// </summary>
		public App()
		{
			// Global handler for uncaught exceptions.
			UnhandledException += Application_UnhandledException;

			// Standard XAML initialization
			InitializeComponent();

			// Phone-specific initialization
			InitializePhoneApplication();

			// Show graphics profiling information while debugging.
			if (Debugger.IsAttached)
			{
				// Display the current frame rate counters.
				Current.Host.Settings.EnableFrameRateCounter = true;

				// Show the areas of the app that are being redrawn in each frame.
				//Application.Current.Host.Settings.EnableRedrawRegions = true;

				// Enable non-production analysis visualization mode,
				// which shows areas of a page that are handed off to GPU with a colored overlay.
				//Application.Current.Host.Settings.EnableCacheVisualization = true;

				// Prevent the screen from turning off while under the debugger by disabling
				// the application's idle detection.
				// Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
				// and consume battery power when the user is not using the phone.
				PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
			}

			Deployment.Current.Dispatcher.BeginInvoke(InitializeWhenFrameCreated);
		}

		public PhoneApplicationFrame GetRootFrame()
		{
			var rootFrame = (PhoneApplicationFrame)RootVisual;
			return rootFrame;
		}

		void InitializeWhenFrameCreated()
		{
			InitializeLanguage();

			/* The Bootstrapper initializes the IoC container, 
			 * creates some default type associations, 
			 * and sets up the viewmodel state preservation system. */
			Bootstrapper bootstrapper = new Bootstrapper();
			bootstrapper.Run();

			var rootFrame = GetRootFrame();
			rootFrame.BackKeyPress += HandleBackKeyPress;
		}

		void HandleBackKeyPress(object sender, CancelEventArgs args)
		{
			var rootFrame = (PhoneApplicationFrame)RootVisual;
			if (rootFrame != null && !rootFrame.BackStack.Any())
			{
				if (AppSettings.Instance.ConfirmAppExit)
				{
					var dialogService = Dependency.Resolve<IMessageService>();
					if (!dialogService.AskOkCancelQuestion(
						AppResources.Question_LeavingAppConfirmation_Message,
						AppResources.Question_LeavingAppConfirmation_Caption))
					{
						args.Cancel = true;
					}
				}
			}
		}

		// Code to execute when the application is launching (eg, from Start)
		// This code will not execute when the application is reactivated
		void Application_Launching(object sender, LaunchingEventArgs e)
		{
			/* Relies on the setting service and therefore 
			 * must be performed after the bootstrapper has been initialized. */
			Deployment.Current.Dispatcher.BeginInvoke(RemoveDeactivationTimeSetting);

			Deployment.Current.Dispatcher.BeginInvoke(() =>
			{
				var messageBus = Dependency.Resolve<IMessenger>();
				messageBus.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Launching));
			});
		}

		// Code to execute when the application is activated (brought to foreground)
		// This code will not execute when the application is first launched
		void Application_Activated(object sender, ActivatedEventArgs e)
		{
			fastResumeExpired = HasFastResumeExpired();

			var messageBus = Dependency.Resolve<IMessenger>();
			messageBus.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Activated));
		}

		// Code to execute when the application is deactivated (sent to background)
		// This code will not execute when the application is closing
		void Application_Deactivated(object sender, DeactivatedEventArgs e)
		{
			SaveDeactivationTime();

			var messageBus = Dependency.Resolve<IMessenger>();
			messageBus.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Deactivated));
		}

		// Code to execute when the application is closing (eg, user hit Back)
		// This code will not execute when the application is deactivated
		void Application_Closing(object sender, ClosingEventArgs e)
		{
			RemoveDeactivationTimeSetting();

			var messageBus = Dependency.Resolve<IMessenger>();
			messageBus.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Closing));
		}

		void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
		{
			if (Debugger.IsAttached)
			{
				Debugger.Break();
			}
			e.Handled = true;
		}

		void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
		{
			if (Debugger.IsAttached)
			{
				Debugger.Break();
			}
		}

		#region Phone application initialization

		// Avoid double-initialization
		bool phoneApplicationInitialized;

		// Do not add any additional code to this method
		void InitializePhoneApplication()
		{
			if (phoneApplicationInitialized)
			{
				return;
			}

			//var rootFrame = GetRootFrame();
			// Create the frame but don't set it as RootVisual yet; this allows the splash
			// screen to remain active until the application is ready to render.
			//rootFrame = new PhoneApplicationFrame();
			//rootFrame.Navigated += CompleteInitializePhoneApplication;

			// Handle navigation failures
			//rootFrame.NavigationFailed += RootFrame_NavigationFailed;

			// Handle reset requests for clearing the backstack
			//rootFrame.Navigated += CheckForResetNavigation;

			// Ensure we don't initialize again
			phoneApplicationInitialized = true;
		}

		// Do not add any additional code to this method
		void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
		{
			var rootFrame = GetRootFrame();

			// Set the root visual to allow the application to render
//			if (RootVisual != rootFrame)
//			{
//				RootVisual = rootFrame;
//			}

			// Remove this handler since it is no longer needed
			rootFrame.Navigated -= CompleteInitializePhoneApplication;
		}

		void CheckForResetNavigation(object sender, NavigationEventArgs e)
		{
			// If the app has received a 'reset' navigation, then we need to check
			// on the next navigation to see if the page stack should be reset
			if (e.NavigationMode == NavigationMode.Reset)
			{
				var rootFrame = GetRootFrame();
				rootFrame.Navigated += ClearBackStackAfterReset;
			}
		}

		void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
		{
			var rootFrame = GetRootFrame();

			// Unregister the event so it doesn't get called again
			rootFrame.Navigated -= ClearBackStackAfterReset;

			// Only clear the stack for 'new' (forward) and 'refresh' navigations
			if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
			{
				return;
			}

			// For UI consistency, clear the entire page stack
			while (rootFrame.RemoveBackEntry() != null)
			{
				; // do nothing
			}
		}

		#endregion

		// Initialize the app's font and flow direction as defined in its localized resource strings.
		//
		// To ensure that the font of your application is aligned with its supported languages and that the
		// FlowDirection for each of those languages follows its traditional direction, ResourceLanguage
		// and ResourceFlowDirection should be initialized in each resx file to match these values with that
		// file's culture. For example:
		//
		// AppResources.es-ES.resx
		//    ResourceLanguage's value should be "es-ES"
		//    ResourceFlowDirection's value should be "LeftToRight"
		//
		// AppResources.ar-SA.resx
		//     ResourceLanguage's value should be "ar-SA"
		//     ResourceFlowDirection's value should be "RightToLeft"
		//
		// For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072.
		//
		void InitializeLanguage()
		{
			try
			{
				var rootFrame = GetRootFrame();
				// Set the font to match the display language defined by the
				// ResourceLanguage resource string for each supported language.
				//
				// Fall back to the font of the neutral language if the Display
				// language of the phone is not supported.
				//
				// If a compiler error is hit then ResourceLanguage is missing from
				// the resource file.
				rootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage);

				// Set the FlowDirection of all elements under the root frame based
				// on the ResourceFlowDirection resource string for each
				// supported language.
				//
				// If a compiler error is hit then ResourceFlowDirection is missing from
				// the resource file.
				var flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
				rootFrame.FlowDirection = flow;
			}
			catch
			{
				// If an exception is caught here it is most likely due to either
				// ResourceLangauge not being correctly set to a supported language
				// code or ResourceFlowDirection is set to a value other than LeftToRight
				// or RightToLeft.

				if (Debugger.IsAttached)
				{
					Debugger.Break();
				}

				throw;
			}
		}

		#region Fast Resume Support
		enum SessionType
		{
			None,
			Home,
			DeepLink
		}

		SessionType sessionType = SessionType.None;

		/* true when the app is being fast resumed. */
		bool lastNavigationWasReset;

		/* true when the period after deactivation exceeds 5 minutes. */
		bool fastResumeExpired;

		readonly TimeSpan fastResumeExpiryTime = TimeSpan.FromMinutes(5);

		bool shouldClearJournal = true;

		const string DeactivationTimeKey = "DeactivationTime";

		void SaveDeactivationTime()
		{
			var settingsService = Dependency.Resolve<ISettingsService>();
			settingsService.SetSetting(DeactivationTimeKey, DateTimeOffset.Now);
		}

		void RemoveDeactivationTimeSetting()
		{
			var settingsService = Dependency.Resolve<ISettingsService>();
			settingsService.SetSetting<object>(DeactivationTimeKey, null);
		}

		bool HasFastResumeExpired()
		{
			DateTimeOffset? lastDeactivated;

			var settingsService = Dependency.Resolve<ISettingsService>();
			lastDeactivated = settingsService.GetSetting<DateTimeOffset?>(DeactivationTimeKey, null);
			if (lastDeactivated.HasValue)
			{
				return false;
			}

			TimeSpan duration = DateTimeOffset.Now.Subtract(lastDeactivated.Value);

			return TimeSpan.FromSeconds(duration.TotalSeconds)
						> fastResumeExpiryTime;
		}
		#endregion

		const string MainViewUrl = "/MainView.xaml";

		void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e)
		{
			#region Fast Resume Support

			string url = e.Uri.ToString();

			if (sessionType == SessionType.None && e.NavigationMode == NavigationMode.New)
			{
				/* App is launching. */

				if (url.Contains("?"))
				{
					sessionType = SessionType.DeepLink;
				}
				else if (url.Contains(MainViewUrl))
				{
					sessionType = SessionType.Home;
				}
			}

			if (e.NavigationMode == NavigationMode.Reset)
			{
				/* The current navigation is the result of a Fast App Resume. 
				 * If so the URI is different from the current page URI 
				 * then another navigation will follow. */
				lastNavigationWasReset = true;
			}
			else if (e.NavigationMode == NavigationMode.New && lastNavigationWasReset)
			{
				/* This block will run once if the previous navigation 
				 * was the result of a Fast App Resume. */
				lastNavigationWasReset = false;

				if (url.Contains("?"))
				{
					/* We reach this point if a secondary entry point was used, 
					 * such as via the secondary tile 
					 * created in FastAppResumeViewModel.cs */

					sessionType = SessionType.DeepLink;
					/* The page navigation stack will be cleared. */
				}
				else if (url.Contains(MainViewUrl))
				{
					if (sessionType == SessionType.DeepLink)
					{
						/* When the app was previously launched via a deep link URI
						 * and relaunched via its primary tile, 
						 * the back stack needs to be cleared. */
						sessionType = SessionType.Home;
					}
					else
					{
						if (!fastResumeExpired)
						{
							/* The app was previously launched via its primary tile 
							 * and relaunched via its primary tile. 
							 * Cancel the navigation to resume. */
							e.Cancel = true;
							shouldClearJournal = false;
						}
					}
				}

				fastResumeExpired = false;
			}
			#endregion
		}

		bool previousNavigationWasReset;

		void RootFrame_Navigated(object sender, NavigationEventArgs e)
		{
			#region Fast Resume Support

			NavigationMode mode = e.NavigationMode;

			if (previousNavigationWasReset && mode == NavigationMode.New)
			{
				if (shouldClearJournal)
				{
					Debug.WriteLine("Clearing history.");

					/* Clear the entire page stack. */
					var rootFrame = (PhoneApplicationFrame)RootVisual;
					while (rootFrame.RemoveBackEntry() != null)
					{
						/* Nothing to do. */
					}
				}
				else
				{
					shouldClearJournal = true;
				}
			}
			else
			{
				previousNavigationWasReset = mode == NavigationMode.Reset;
			}

			#endregion
		}
	}
}